跳到主要内容

SpringCloud Config 配置服务

参考资料 官方文档 参考资料 Spring Cloud Config 原理简介和实现

SpringCloud Config 服务是什么

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。

在 Spring Cloud 中,有分布式配置中心组件 Spring cloud Config,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程 Git仓库中。

在 Spring Cloud Config 组件中,分两个角色

  • ConfigServer
  • ConfigClient

Config Server 是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用 Git存储配置文件内容,也可以使用 SVN存储,或者是本地文件存储。

Config Client 是 Config Server 的客户端,用于操作存储在 Config Server中的配置内容。

微服务在启动时会请求 Config Server 获取配置文件的内容,请求到后再启动容器。

说白了主要解决如下几个问题

1、因为配置文件分散在各个项目中不方便维护 2、配置文件的安全性问题 3、修改完配置文件无法立即生效

搭建 ConfigServer

因为网络问题,这里使用 Gitee 新建一个库

然后拉取到本地

mkdir temp-config
cd temp-config
git init # 如果已经在远程创建了可以直接 clone
# git clone git@gitee.com:alsritter/temp-config.git

cat > README.md << EOF
> This is the configuration center Repository!
> EOF

git add README.md
git commit -m "first commit"
git remote add origin git@gitee.com:alsritter/temp-config.git
git push -u origin master

再在里面随便创建几个文件

例如这个 config-dev.yml 里面写如下内容(随便写的)

config:
info: "master branch,springcloud-config/config-dev.yml version=7"

添加服务端依赖

<!-- 还是需要把这个注册进中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!-- 配置服务端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

ConfigServer 的配置文件

原理是绑定一个远程库

eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/

server:
port: 3344

spring:
application:
name: config
cloud:
config:
server:
git:
# 本地仓库可以不设置
# basedir: D:\JavaProject\studycloud\temp-config # 本地仓库地址
# username: alsritter # 远程仓库名,如果没有配置 SSH 需要这东西
# password: 123 # 远程仓库密码
uri: https://gitee.com/alsritter/temp-config.git # 远程仓库 uri
# 如果配置文件丢到了子目录,可以添加搜索目录(默认情况下,仅搜索根目录)
search-paths:
- cloud-config
# 读取分支
label: master

在启动类上加入 @EnableConfigServer 注解

@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}

按照上面 git 上的路径访问,可以看到已经能正常访问文件里面的内容了

不过注意,这些配置文件名有一定的读取规则,如下所示

# config 服务也有一定的读取规则
# {label}:分支
# {profile}:一般用于指定环境,如prod,dev,uat等
# {application}:应用的名字,在以后客户端读取会用到
# 它对应上面 ConfigServer 的 application.yml 上的配置

# 有如下几种规则,只需选择一种最喜欢的就行了
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml # 一般使用这种
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

/git分支/文件名称-哪个环境的配置.yml

# 例如(这里并没有指定环境,所以随便写为 hhh)
http://localhost:8091/master/customer-hhh.yml

搭建 ConfigClient

注意,这里使用的是 config-client

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>

配置文件,虽然使用了 Config-server 但是不是所有的配置都可以省略掉的,一些基本配置还是不能省略,例如连接上 Eureka 的配置不能省

这个服务本地的配置文件名也要从 application.yml 改成 bootstrap.yml 因为需要一开始就加载配置文件,更改成这个名称之后 SpringBoot 会在一开始就执行这个配置文件

注:bootstrap/ application 的区别

Spring Cloud 会创建一个"Bootstrap Context",作为 Spring 应用的 Application Context 的父上下文。初始化的时候 BootstrapContext 负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的 Environment。

Bootstrap 属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context 和 Application Context 有着不同的约定所以新增了一个 bootstrap.yml 文件,保证 Bootstrap Context 和 Application Context 配置的分离。

因此,对比 application 配置文件,bootstrap 配置文件具有以下几个特性。

  • boostrap 由父 ApplicationContext 加载,比 application 优先加载
  • boostrap 里面的属性不能被覆盖

在创建的 bootstrap.yml 里面写如下内容

server:
port: 3355

spring:
application:
name: "config-client"
cloud:
# Config客户端配置
config:
label: master # 分支名称
name: config # 配置文件名称
profile: dev # 读取后缀名称
# 上述3个综合 例如:master分支上 config-dev.yml 的配置文件被读取 http://localhost:3344/master/config-dev.yml
uri: http://localhost:3344 # 配置中心地址

# 服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka

编写一个 Controller 来测试是否能读取到服务端提供的配置

@RestController
public class ConfigClientController {
@Value("${config.info}")
private String configInfo; //要访问的3344上的信息

@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}

访问测试,可以读取到远程配置文件的内容

动态更新配置

修改了远程仓库的配置文件,刷新 ConfigServer 可以发现直接变更了(毕竟直连 Gitee),但是刷新 ConfigClient 却发现没有变动,除非手动重启客户端服务才能读取到最新的配置,而这样每次都需要手动重启太麻烦了,所以需要设置一个动态刷新机制

首先在客户端引入这个监控依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

添加暴露的端口

#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"

然后添加一个 @RefreshScope 注解(config 包提供的),这个注解的作用是让 Bean 可以在运行时刷新,并且使用它们的任何组件都将在下一个方法调用上获得一个新实例,该实例将完全初始化并注入所有依赖项。(就是支持动态刷新当前 Bean)

@RestController
@RefreshScope
public class ConfigClientController {
@Value("${config.info}")
private String configInfo; //要访问的3344上的信息

@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}

这时还是无法动态刷新客户端的配置,还需要手动通知一下上面的客户端,当前已经修改了配置文件了

发送一个 post 请求给客户端,告诉它需要重新注入 Bean了

# 注意 powershell 执行不了,但是可以在 cmd 和 linux 上执行
curl -X POST http://localhost:3355/actuator/refresh

再次刷新可以看到内容已经改变